package typ

import (
	"fmt"
)

// ObjectType represents the type of a Scheme object
type ObjectType int

const (
	NULL ObjectType = iota
	BOOLEAN
	NUMBER
	INTEGER
	RATIONAL
	REAL
	STRING
	SYMBOL
	PAIR
	CLOSURE
	BUILTIN
)

// Object is the interface for all Scheme objects
type Object interface {
	Type() ObjectType
	String() string
}

// Null represents the empty value in Scheme (nil or ())
type Null struct{}

func (n *Null) Type() ObjectType { return NULL }
func (n *Null) String() string   { return "()" }

// Boolean represents boolean values in Scheme
type Boolean struct {
	Value bool
}

func (b *Boolean) Type() ObjectType { return BOOLEAN }
func (b *Boolean) String() string {
	if b.Value {
		return "#t"
	}
	return "#f"
}

// Number is the interface for all numeric types
type Number interface {
	Object
	Value() float64
}

// Integer represents integer types in Scheme
type Integer struct {
	IntValue int64
}

func (i *Integer) Type() ObjectType { return INTEGER }
func (i *Integer) String() string   { return fmt.Sprintf("%d", i.IntValue) }
func (i *Integer) Value() float64   { return float64(i.IntValue) }

// Rational represents rational number types in Scheme
type Rational struct {
	Numerator   int64
	Denominator int64
}

func (r *Rational) Type() ObjectType { return RATIONAL }
func (r *Rational) String() string {
	// Simplify the fraction
	gcd := gcd(r.Numerator, r.Denominator)
	num := r.Numerator / gcd
	den := r.Denominator / gcd
	
	// If denominator is 1, output as integer
	if den == 1 {
		return fmt.Sprintf("%d", num)
	}
	return fmt.Sprintf("%d/%d", num, den)
}
func (r *Rational) Value() float64 { return float64(r.Numerator) / float64(r.Denominator) }

// Add implements addition for rational numbers
func (r *Rational) Add(other *Rational) *Rational {
	numerator := r.Numerator*other.Denominator + other.Numerator*r.Denominator
	denominator := r.Denominator * other.Denominator
	return &Rational{Numerator: numerator, Denominator: denominator}
}

// Subtract implements subtraction for rational numbers
func (r *Rational) Subtract(other *Rational) *Rational {
	numerator := r.Numerator*other.Denominator - other.Numerator*r.Denominator
	denominator := r.Denominator * other.Denominator
	return &Rational{Numerator: numerator, Denominator: denominator}
}

// Multiply implements multiplication for rational numbers
func (r *Rational) Multiply(other *Rational) *Rational {
	numerator := r.Numerator * other.Numerator
	denominator := r.Denominator * other.Denominator
	return &Rational{Numerator: numerator, Denominator: denominator}
}

// Divide implements division for rational numbers
func (r *Rational) Divide(other *Rational) *Rational {
	numerator := r.Numerator * other.Denominator
	denominator := r.Denominator * other.Numerator
	// Ensure denominator is positive
	if denominator < 0 {
		numerator = -numerator
		denominator = -denominator
	}
	return &Rational{Numerator: numerator, Denominator: denominator}
}

// Real represents real number types in Scheme
type Real struct {
	RealValue float64
}

func (r *Real) Type() ObjectType { return REAL }
func (r *Real) String() string {
	// If it's an integer, remove the decimal point
	if float64(int64(r.RealValue)) == r.RealValue {
		return fmt.Sprintf("%d", int64(r.RealValue))
	}
	return fmt.Sprintf("%g", r.RealValue)
}
func (r *Real) Value() float64 { return r.RealValue }

// Helper function: Calculate greatest common divisor
func gcd(a, b int64) int64 {
	// Ensure b is not negative
	if b < 0 {
		b = -b
	}
	if a < 0 {
		a = -a
	}
	// Euclidean algorithm
	for b != 0 {
		a, b = b, a%b
	}
	return a
}

// String represents strings in Scheme
type String struct {
	Value string
}

func (s *String) Type() ObjectType { return STRING }
func (s *String) String() string   { return fmt.Sprintf("\"%s\"", s.Value) }

// Symbol represents symbols in Scheme
type Symbol struct {
	Value string
}

func (s *Symbol) Type() ObjectType { return SYMBOL }
func (s *Symbol) String() string   { return s.Value }

// Pair represents list pairs in Scheme (car/cdr structure)
type Pair struct {
	Car Object
	Cdr Object
}

func (p *Pair) Type() ObjectType { return PAIR }
func (p *Pair) String() string {
	return fmt.Sprintf("(%s)", pairToString(p, true))
}

// pairToString recursively converts a Pair to string representation
func pairToString(p *Pair, first bool) string {
	result := ""
	if !first {
		result += " "
	}
	result += p.Car.String()

	// Handle the cdr part
	if p.Cdr == nil || p.Cdr.Type() == NULL {
		// cdr is empty
		return result
	} else if cdrPair, ok := p.Cdr.(*Pair); ok {
		// cdr is another Pair
		return result + pairToString(cdrPair, false)
	} else {
		// cdr is another type, use dot notation
		return fmt.Sprintf("%s . %s", result, p.Cdr.String())
	}
}

// Closure represents user-defined functions
type Closure struct {
	Params *Pair        // parameter list
	Body   Object       // function body
	Env    *Environment // environment at function definition time
}

func (c *Closure) Type() ObjectType { return CLOSURE }
func (c *Closure) String() string {
	return "#<closure>"
}

// BuiltinFunction is the type definition for built-in functions
type BuiltinFunction func(args []Object) Object

// Builtin represents built-in functions
type Builtin struct {
	Fn BuiltinFunction
}

func (b *Builtin) Type() ObjectType { return BUILTIN }
func (b *Builtin) String() string   { return "#<builtin>" }

// Predefined constants
var (
	Nil   = &Null{}
	True  = &Boolean{true}
	False = &Boolean{false}
)
